home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / filesy~1 / mfs609s.zoo / minixfs / minixfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-25  |  33.3 KB  |  1,451 lines

  1. /* This file is part of 'minixfs' Copyright 1991,1992,1993 S.N.Henson */
  2.  
  3. #include "minixfs.h"
  4. #include "proto.h"
  5. #include "global.h"
  6.  
  7. FILESYS minix_filesys = {
  8.     (FILESYS *) 0,
  9.     FS_CASESENSITIVE | FS_LONGPATH,
  10.     m_root, m_lookup, m_creat, m_getdev,
  11.     m_getxattr, m_chattr, m_chown,
  12.     m_chmode, m_mkdir,
  13.     m_rmdir, m_remove, m_getname,
  14.     m_rename, m_opendir, m_readdir,
  15.     m_rewinddir, m_closedir,
  16.     m_pathconf, m_dfree,
  17.     m_wlabel, m_rlabel,
  18.     m_symlink, m_readlink,
  19.     m_hardlink, m_fscntl, m_dskchng,
  20.     m_release,m_dupcookie
  21. };
  22.  
  23. extern DEVDRV minix_dev;
  24.  
  25. static restore_dev=-1;
  26.  
  27. /*
  28.  * the kernel calls this when it detects a disk change. Note the mnt_flags
  29.  * flag MNT_CHANGE signals that the drive is being changed to force relogging
  30.  * of it's root directory and it should not be completely changed.
  31.  */
  32.  
  33. /*
  34.  * Hack alert: disk changes on mounted filesystems are ungracefully handled.
  35.  * Basically, if it isn't the first forced change then they are ignored. See
  36.  * comment (3) in #if does_not_work_yet for reason. This isn't any great loss
  37.  * because changing mounted filesystems is very naughty (causing various Unix
  38.  * variants to panic, in more senses than one. If Unix doesn't handle it why
  39.  * should I? ). Actually since changes, by necessity force the mounted 
  40.  * structure to be destroyed, there isn't much we can gracefully do.
  41.  */
  42.  
  43. long m_dskchng(d)
  44. int d;
  45. {
  46.     FILEPTR *f, **last;
  47.     super_info *psblk;
  48.     char ignore;
  49.     psblk=super_ptr[d];
  50.  
  51.     TRACE("Disk Change drive %c",d+'A');
  52.  
  53.     if(psblk && psblk!=DFS)
  54.     {
  55.         ignore = psblk->mnt_flags & MNT_CHANGE;
  56.         /* If mounted then (for now) ignore changes */
  57.         if(!ignore && psblk->mnt_inode) return 0;
  58.     }
  59.     else ignore=0;
  60.  
  61.     if(psblk && psblk!=DFS && !ignore)
  62.     {
  63.         Kfree(psblk->ibitmap);
  64.  
  65. #if does_not_work_yet
  66.  
  67.         /* Need to do three things:
  68.          * 1. Make sure all children are no longer mounted.
  69.          * 2. Make sure filesystem is no longer searched in lookups
  70.          *    of parent device.
  71.          * 3. (sigh) Force media change of all descendents without
  72.          *    disturbing mounted structure, so MiNT will reread the
  73.          *    root dirs properly.
  74.          */
  75.  
  76.         if(psblk->mnt_inode)
  77.         /* Umount this filesystem */
  78.         {
  79.           super_info **ublk,*tsblk;
  80.           /* Unlink from list on parent device */
  81.           for(ublk=&super_ptr[psblk->mnt_dev]->mnt_first;*ublk;
  82.                               ublk=&(*ublk)->mnt_next)
  83.             if(*ublk==psblk)
  84.             {
  85.             *ublk=psblk->mnt_next;
  86.             break;
  87.             }
  88.             /* Umount all children */
  89.             for(tsblk=psblk->mnt_first;tsblk;tsblk=tsblk->mnt_next)
  90.                                  tsblk->mnt_inode=0;
  91.         }
  92. #endif
  93.         Kfree(psblk);
  94.     }
  95.     if(!ignore) super_ptr[d]=0;
  96.  
  97.     /* this may affect the m_getname cache, too */
  98.     if (lpath && (d == lroot.dev || d == ldir.dev)) {
  99.         Kfree(lpath);
  100.         lpath = 0;
  101.     }
  102.  
  103.     /* Since the disk has changed always invalidate cache */
  104.     m_invalidate(d);
  105.  
  106.     /* Free any memory associated to file pointers of this drive. */
  107.     last = &firstptr;
  108.     for (f = *last; f != 0; f = *last)
  109.       {
  110.         if (f->fc.dev == d)
  111.           {
  112.         f_cache *fch = (f_cache *) f->devinfo;
  113.         /* The lock structure is shared between the fileptr's.
  114.            Make sure that it is freed only once. */
  115.         if (!f->next || f->next->fc.dev != d
  116.             || f->next->fc.index != f->fc.index)
  117.           {
  118.             LOCK *lck, *nextlck;
  119.             nextlck = *fch->lfirst;
  120.             while ((lck = nextlck) != 0)
  121.               {
  122.             nextlck = lck->next;
  123.             Kfree (lck);
  124.               }
  125.             Kfree (fch->lfirst);
  126.           }
  127.         Kfree (fch);
  128.         /* Remove this fileptr from the list. */
  129.         *last = f->next;
  130.         f->next = 0;
  131.           }
  132.         else
  133.           last = &f->next;
  134.       }
  135.         
  136.     if(!ignore) minix_sanity(d);
  137.  
  138.     return 1;
  139. }
  140.  
  141. /*
  142.  * Note: in the first round of initialisations, we assume that floppy
  143.  * drives (A and B) don't belong to us; but in a later disk change,
  144.  * they may very well be ours, so we remember that. This is means that a
  145.  * minix disk inserted into a drive will be unrecognisable at boot up and
  146.  * a forced disk change is needed. However for MiNT 1.05 (and presumably
  147.  * later) drives are initialised on first access so this isn't needed.
  148.  */
  149.  
  150. long m_root(dev,dir)
  151. int dev;
  152. fcookie *dir;
  153. {
  154.     int ret;
  155.     static first_init = 2;
  156.     extern FILESYS dummy_filesys;
  157.     super_info **psblk;
  158.  
  159.     int drv=dev;
  160.  
  161.     psblk = super_ptr+dev;
  162.  
  163.     ret=0;
  164.  
  165.     if( (kernel->maj_version==0 ) || 
  166.         (kernel->maj_version==1 && kernel->min_version < 5 ) ) {
  167.         /* the first 2 checks (on A: and B:) we fail automatically */
  168.         if (first_init ) {
  169.             --first_init;
  170.             return -1;
  171.         }
  172.     }
  173.  
  174.     /* If not present, see if it's valid */
  175.     if( *psblk || ( dev >= 0 && (ret=minix_sanity(dev)) ) ) {
  176.         if(ret ==-1 || *psblk==DFS ) dir->fs = &dummy_filesys;
  177.         else
  178.         {
  179.             dir->fs=&minix_filesys;
  180.             /* Aux field tells original device */
  181.             dir->aux= dev | AUX_DRV ;
  182.             dir->index=ROOT_INODE;
  183.             /* If mounted trace back to root filesystem */
  184.             while((*psblk)->mnt_inode)
  185.             {
  186.                 dev=(*psblk)->mnt_dev;
  187.                 psblk=super_ptr+dev;
  188.             }
  189.         }
  190.         dir->dev=dev;
  191. DEBUG("m_root %c returned %ld (%c)",drv+'A',dir->index,dir->dev+'A');
  192.         return 0;
  193.  
  194.     }
  195.     return -1;
  196. }
  197.  
  198. long m_lookup(dir,name,entry)
  199. fcookie *dir; 
  200. char *name;
  201. fcookie *entry;
  202. {
  203.  
  204.     if(!*name)
  205.     {
  206.         *entry=*dir;
  207.         entry->aux=0;
  208.         return 0;
  209.     }
  210.  
  211.     if(dir->index==ROOT_INODE && !strcmp(name,".."))
  212.     {
  213.         *entry=*dir;
  214.         /* If mounted treat as a lookup from mount point */
  215.         if( cross_mount(entry) ) return EMOUNT;
  216.         DEBUG("m_lookup: crossing mount point");
  217.         entry->index = search_dir(name,entry->index,entry->dev,FIND);
  218.         if(entry->index < 0 )  return entry->index;
  219.         entry->aux=0;
  220.         return 0;
  221.     }
  222.  
  223.     entry->index=search_dir(name,dir->index,dir->dev,FIND);
  224.     entry->dev=dir->dev;
  225.     if(entry->index < 0 ) return entry->index ;
  226.     entry->aux=0;
  227.     entry->fs=&minix_filesys;
  228.     if( check_mount(entry) ) DEBUG("Crossed mount point");
  229.     return 0;
  230. }
  231.  
  232. long m_creat(dir,name,mode,attr,entry)
  233. fcookie *dir;
  234. char *name;
  235. unsigned mode;
  236. int attr;
  237. fcookie *entry;
  238. {
  239.     long pos;
  240.     d_inode ripnew;
  241.     unshort newfile;
  242.     char *ext;
  243.     
  244.     /* Create dir entry */    
  245.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)
  246.     {    
  247.         return pos;
  248.     }
  249.  
  250.     /* Get new inode */
  251.     if(!(newfile=alloc_inode(dir->dev)))
  252.     {
  253.         DEBUG("m_getdev: no free inodes");
  254.         return EWRITF;
  255.     }
  256.     /* Set up inode */
  257.     bzero(&ripnew,sizeof(d_inode));
  258.  
  259. /* If  creating a file with approriate extensions 
  260.  * automatically give it execute permissions.
  261.  */
  262.     if(do_trans(AEXEC_TOS,dir->dev) && ( ext=strrchr(name,'.') ) )
  263.     {
  264.         ext++;
  265.         if( 
  266.         /* Insert your favourite extensions here */
  267.           !(  Stricmp(ext,"TTP") && Stricmp(ext,"PRG") 
  268.            && Stricmp(ext,"APP") && Stricmp(ext,"TOS") 
  269.            && Stricmp(ext,"ACC") && Stricmp(ext, "GTP")))
  270.                 mode |= 0111;
  271.     }
  272.     ripnew.i_mode= I_REGULAR | mode;
  273.     ripnew.i_uid=Getuid();
  274.     ripnew.i_gid=Getgid();
  275.     ripnew.i_nlinks=1;
  276.  
  277.     ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
  278.     ripnew.i_atime=ripnew.i_mtime;
  279.     ripnew.i_ctime=ripnew.i_mtime;
  280.  
  281.     write_inode(newfile,&ripnew,dir->dev);
  282.     l_write(dir->index,pos,2L,&newfile,dir->dev);
  283.  
  284.     if(cache_mode) l_sync();
  285.  
  286.     entry->fs = dir->fs;
  287.     entry->dev = dir->dev;
  288.     entry->index=newfile;
  289.     entry->aux=0;
  290.     return 0;
  291. }
  292.  
  293. DEVDRV * m_getdev(file,special)
  294. fcookie *file;
  295. long *special;
  296. {
  297.     return(&minix_dev);
  298. }
  299.  
  300. long m_getxattr(file,xattr)
  301. fcookie *file;
  302. XATTR *xattr;
  303. {
  304.         d_inode rip;
  305.     long time_tmp;
  306.     super_info *psblk;
  307.     psblk=super_ptr[file->dev];
  308.     read_inode(file->index,&rip,file->dev);
  309.     /* Minix and gcc use different values for FIFO's */
  310.     if((rip.i_mode & I_TYPE) == I_NAMED_PIPE)
  311.         xattr->mode = S_IFIFO | (rip.i_mode & ALL_MODES);
  312.         else xattr->mode=rip.i_mode;
  313.  
  314.     /* We could potentially have trouble with symlinks too */
  315. #if I_SYMLINK != S_IFLNK
  316.     if( (rip.i_mode & I_TYPE) == I_SYMLINK)
  317.         xattr->mode = S_IFLNK | (rip.i_mode & ALL_MODES);
  318. #endif
  319.  
  320.     /* Fake attr field a bit , to keep TOS happy */
  321.     if(IS_DIR(rip))xattr->attr=FA_DIR;
  322.     else xattr->attr=(rip.i_mode & 0222) ? 0 : FA_RDONLY;
  323.  
  324.         xattr->index=file->index;
  325.         xattr->dev=file->dev;
  326. #if 0
  327.     /* Char and block special files need major/minor device nos filled in */
  328.     if(((rip.i_mode & I_TYPE) == I_CHAR_SPECIAL )||
  329.         ( (rip.i_mode & I_TYPE) == I_BLOCK_SPECIAL ))
  330.             xattr->reserved=rip.i_zone[0];
  331.         else xattr->reserved=0;
  332. #endif
  333.         xattr->nlink=rip.i_nlinks;
  334.         xattr->uid=rip.i_uid;
  335.         xattr->gid=rip.i_gid;
  336.         xattr->size=rip.i_size;
  337.     xattr->blksize = BLOCK_SIZE;
  338. /* Note: the nblocks calculation is accurate only if the file is
  339.  * contiguous. It usually will be, and if it's not, it shouldn't
  340.  * matter ('du' will return values that are slightly too high)
  341.  */
  342.     xattr->nblocks = (xattr->size + (BLOCK_SIZE-1)) / BLOCK_SIZE;
  343.     if (xattr->nblocks >= psblk->dzpi)
  344.         xattr->nblocks++;    /* correct for the indirection block */
  345.     if (xattr->nblocks > psblk->ndbl) {
  346.         xattr->nblocks++;    /* correct for double indirection block */
  347.         xattr->nblocks += ((xattr->nblocks-(psblk->ndbl+2))/psblk->zpind);
  348.                 /* and single indirection blocks */
  349.     }
  350.  
  351.     time_tmp=Dostime(_corr(rip.i_mtime));
  352.     xattr->mtime=time_tmp >> 16;
  353.     xattr->mdate=time_tmp & (0xffff);
  354.     time_tmp=Dostime(_corr(rip.i_atime));
  355.     xattr->atime=time_tmp >> 16;
  356.     xattr->adate=time_tmp & (0xffff);
  357.     time_tmp=Dostime(_corr(rip.i_ctime));
  358.     xattr->ctime=time_tmp >> 16;
  359.     xattr->cdate=time_tmp & (0xffff);
  360.  
  361.     xattr->reserved1=0;
  362.     xattr->reserved2=0;
  363.     xattr->reserved3[0]=0;
  364.     xattr->reserved3[1]=0;
  365.  
  366.     return 0;
  367. }
  368.  
  369. long m_chown(file, uid , gid)
  370. fcookie *file;
  371. int uid,gid;
  372. {
  373.  
  374.     d_inode rip;
  375.     read_inode(file->index,&rip,file->dev);
  376.      if(uid!=-1)rip.i_uid=uid;
  377.     if(gid!=-1)rip.i_gid=gid;
  378.     rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  379.     write_inode(file->index,&rip,file->dev);
  380.     if(cache_mode) l_sync();
  381.     return 0;
  382. }
  383.  
  384. long m_chmode(file, mode)
  385. fcookie *file;
  386. unsigned mode;
  387. {
  388.         d_inode rip;
  389.     super_info *psblk=super_ptr[file->dev];
  390.  
  391.         read_inode(file->index,&rip,file->dev);
  392.  
  393.         rip.i_mode=(rip.i_mode & I_TYPE)|(mode & ALL_MODES);                
  394.     if(psblk->version)rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  395.     write_inode(file->index,&rip,file->dev);
  396.     if(cache_mode) l_sync();
  397.         return 0;
  398. }
  399.  
  400.  
  401. long m_mkdir(dir,name,mode)
  402. fcookie *dir;
  403. char *name;
  404. unsigned mode;
  405. {
  406.     unshort newdir;
  407.     d_inode rip,ripnew;
  408.     long pos;
  409.     int incr;
  410.     dir_struct blank[MAX_INCREMENT*2];
  411.     incr=super_ptr[dir->dev]->increment;
  412.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)return pos;
  413.     read_inode(dir->index,&rip,dir->dev);
  414.     if(rip.i_nlinks>=MINIX_MAX_LINK)return EACCDN;
  415.     /* Get new inode */
  416.     if(!(newdir=alloc_inode(dir->dev)))return EACCDN;
  417.  
  418.     /* Set up inode */
  419.     bzero(&ripnew,sizeof(d_inode));
  420.     ripnew.i_mode=I_DIRECTORY | (mode & 0777);
  421.     ripnew.i_uid=Getuid();
  422.     ripnew.i_gid=Getgid();
  423.     ripnew.i_nlinks=2;
  424.     ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
  425.     ripnew.i_ctime=ripnew.i_mtime;
  426.     ripnew.i_atime=ripnew.i_mtime;
  427.     write_inode(newdir,&ripnew,dir->dev);
  428.  
  429.     /* Set up new directory */
  430.     strcpy(blank[0].d_name,".");
  431.     blank[0].d_inum=newdir;
  432.     strcpy(blank[incr].d_name,"..");
  433.     blank[incr].d_inum=dir->index;
  434.     if(l_write((unsigned)newdir,-1L,(long)(DIR_ENTRY_SIZE*2*incr),
  435.         blank,dir->dev)!=(incr*DIR_ENTRY_SIZE*2) )
  436.     {
  437.         ripnew.i_mode=0;
  438.         ripnew.i_nlinks=0;
  439.         write_inode(newdir,&ripnew,dir->dev);
  440.         free_inode(newdir,dir->dev);
  441.         if(cache_mode) l_sync();
  442.         return EACCDN;
  443.     }
  444.     rip.i_nlinks++;
  445.     write_inode(dir->index,&rip,dir->dev);
  446.     l_write(dir->index,pos,2L,&newdir,dir->dev);
  447.  
  448.     if(cache_mode) l_sync();
  449.  
  450.     return(0);
  451. }
  452.  
  453. long m_rmdir(dir,name)
  454. fcookie *dir;
  455. char *name;
  456. {
  457.     long chunk,left;
  458.     long inum;
  459.     int i,incr;
  460.     super_info *psblk;
  461.     d_inode rip,rip2;
  462.     if((inum=search_dir(name,dir->index,dir->dev,FIND))<0)return inum;
  463.  
  464.     /* Is anything mounted on this dir ? */
  465.     for(psblk=super_ptr[dir->dev]->mnt_first;psblk;psblk=psblk->mnt_next)
  466.         if(psblk->mnt_inode==inum)
  467.         {
  468.             DEBUG("m_rmdir: can't delete mount point");
  469.             return EACCDN;
  470.         }
  471.  
  472.     read_inode(inum,&rip,dir->dev);
  473.     read_inode(dir->index,&rip2,dir->dev);
  474.     if(!IS_DIR(rip))return EFILNF;
  475.     incr=super_ptr[dir->dev]->increment;
  476.     /* Check if dir is actually empty */
  477.     for(chunk=0;(left=next_zone(&rip,chunk,&temp,dir->dev)/DIR_ENTRY_SIZE);
  478.         chunk++)
  479.     {
  480.         for(i=0;i<left;i+=incr)
  481.            if (temp.bdir[i].d_inum
  482.                && (temp.bdir[i].d_name[0] != '.'
  483.                || temp.bdir[i].d_name[1] != 0)
  484.                && (temp.bdir[i].d_name[0] != '.'
  485.                || temp.bdir[i].d_name[1] != '.'
  486.                || temp.bdir[i].d_name[2] != 0))
  487.             return EACCDN ;
  488.     }
  489.     if(!inode_busy(inum,dir->dev,1))
  490.     {
  491.         trunc_inode(&rip,dir->dev,0L,0);
  492.         rip.i_mode=0;
  493.         free_inode(inum,dir->dev);
  494.     }
  495.     rip.i_nlinks=0;
  496.     write_inode(inum,&rip,dir->dev);
  497.     read_inode(dir->index,&rip,dir->dev);
  498.     rip.i_mtime=Unixtime(Timestamp(), Datestamp());
  499.     rip.i_nlinks--;
  500.     write_inode(dir->index,&rip,dir->dev);
  501.     search_dir(name,dir->index,dir->dev,KILL);
  502.  
  503.     if( lpath && (ldir.dev==dir->dev) && (ldir.index==inum) )
  504.     {
  505.         Kfree(lpath);
  506.         lpath=0;
  507.     }
  508.  
  509.     if(cache_mode) l_sync();
  510.  
  511.     return(0);
  512. }
  513.  
  514. /* Unix-like unlink ... only works on regular files and symlinks but it should
  515.  * be safe to unlink an open file
  516.  */
  517.  
  518. long m_remove(dir,name)
  519. fcookie *dir;
  520. char *name;
  521. {
  522.     long inum,ret;
  523.     d_inode rip;
  524.     inum=search_dir(name,dir->index,dir->dev,FIND);
  525.     if(inum<0) return inum;
  526.     read_inode(inum,&rip,dir->dev);
  527.     if(!IS_REG(rip) && ((rip.i_mode & I_TYPE) != I_SYMLINK) ) return EACCDN;
  528.  
  529.     if((ret=search_dir(name,dir->index,dir->dev,KILL))<0) return ret;    
  530.     if(--rip.i_nlinks==0)
  531.     {
  532.         if(!inode_busy(inum,dir->dev,1)) /* Is inode busy ? */
  533.         {
  534.             trunc_inode(&rip,dir->dev,0L,0);
  535.             rip.i_mode=0;
  536.             free_inode(inum,dir->dev);
  537.         }
  538.     }
  539.     write_inode(inum,&rip,dir->dev);
  540.  
  541.     if(cache_mode) l_sync();
  542.  
  543.     return(0);
  544. }
  545.  
  546. /* This function is inefficient, it uses the standard sys V method of 
  547.  * finding out the pathname of the cwd : for each part of the path, search
  548.  * the parent for a link with the same inode number as '..' , append this to the
  549.  * path until we get to root dir, then reverse order of dirs. This way no 
  550.  * temporary buffers are allocated which could overflow or kmalloc to fail ...
  551.  */
  552.  
  553. /* In fact its so inefficient a mini-cache remembers the last call info */
  554.  
  555. long m_getname(root,dir,pathname,length)
  556. fcookie *root,*dir;
  557. char *pathname;
  558. short length;
  559. {
  560.     long inum,pinum;
  561.     unsigned dev;
  562.     int chunk;
  563.     long left;
  564.     int incr;
  565.     short plength;
  566.     super_info *psblk;
  567.     psblk=super_ptr[dir->dev];
  568.  
  569.     if(no_length) length=PATH_MAX;
  570.  
  571.     if(lpath && lroot.dev==root->dev && 
  572.         lroot.index==root->index && ldir.dev==dir->dev && 
  573.         ldir.index==dir->index)
  574.     {
  575.         TRACE("m_getname: cache hit");
  576.         if(length <= llength) return ENAMETOOLONG;
  577.         strcpy(pathname,lpath);
  578.         return 0;
  579.     }
  580.  
  581.     *pathname=0;
  582.  
  583.     if( dir->dev==root->dev && dir->index==root->index) return 0;
  584.  
  585.     incr=psblk->increment;
  586.     inum=dir->index;
  587.     dev=dir->dev;
  588.     plength=0;
  589.     if(inum==ROOT_INODE && psblk->mnt_inode)
  590.     {
  591.         dev=psblk->mnt_dev;
  592.         inum=psblk->mnt_inode;
  593.         psblk=super_ptr[dev];
  594.     }
  595.  
  596.     while( inum!=root->index && inum!= ROOT_INODE )
  597.     {
  598.         d_inode rip;
  599.         bufr *tmp;
  600.         pinum=search_dir("..",inum,dev,FIND); 
  601.         /* Parent inum */
  602.  
  603.         if(pinum < 0) /* If this happens we're in trouble */
  604.         {
  605.             ALERT("No .. in inode %d , drive %c",inum,dir->dev+'A');
  606.             return pinum;
  607.         }
  608.         read_inode(pinum,&rip,dev);
  609.         for(chunk=0;
  610.         (left=cnext_zone(&rip,chunk,&tmp,dev)/DIR_ENTRY_SIZE) &&
  611.         inum!=pinum ;chunk++)
  612.         {
  613.             char tname[MNAME_MAX+1];
  614.             int i;
  615.             for(i=0;i<left && inum!=pinum ;i+=incr)
  616.             if(tmp->bdir[i].d_inum==inum)
  617.             {
  618.                 strncpy(tname,tmp->bdir[i].d_name,MMAX_FNAME(incr));
  619.                 tname[MMAX_FNAME(incr)]=0;
  620.                 strrev(tname);
  621.                 plength+=strlen(tname)+1;
  622.                 if(length <= plength) return ENAMETOOLONG;
  623.                 strcat(pathname,tname);
  624.                 strcat(pathname,"\\");
  625.                 inum=pinum;
  626.             }
  627.         }
  628.         if(left==0 && inum!=pinum) {
  629.             ALERT("m_getname inode %d orphaned or bad ..",inum);
  630.             return EINTRN;
  631.         }
  632.         /* Cross mount point if possible */
  633.         if(inum==ROOT_INODE && psblk->mnt_inode)
  634.         {
  635.             dev=psblk->mnt_dev;
  636.             inum=psblk->mnt_inode;
  637.             psblk=super_ptr[dev];
  638.         }
  639.     }
  640.     if(inum==ROOT_INODE && root->index!=ROOT_INODE)
  641.     {
  642.         DEBUG("m_getname: Hmmmm root is not a parent of dir");
  643.         return EINTRN;
  644.     }
  645.     strrev(pathname);
  646.     if(lpath)Kfree(lpath);
  647.     if( (lpath=Kmalloc(strlen(pathname)+1)) )
  648.     {
  649.         strcpy(lpath,pathname);
  650.         llength=plength;
  651.     }
  652.  
  653.     lroot=*root;
  654.     ldir=*dir;
  655.     return 0;
  656. }
  657.  
  658.  
  659. long m_opendir(dirh,flag)
  660. DIR *dirh;
  661. int flag;
  662. {
  663.     dirh->index=0;
  664.     return 0;
  665. }
  666.  
  667. long m_readdir(dirh,name,namelen,fc)
  668. DIR *dirh;
  669. char *name;
  670. int namelen;
  671. fcookie *fc;
  672. {
  673.         d_inode rip;
  674.     bufr *tmp;
  675.     unsigned entry,chunk;
  676.     super_info *psblk;
  677.     long limit;
  678.     int flag,incr;
  679.     psblk=super_ptr[dirh->fc.dev];
  680.     if(dirh->flags) flag=do_trans(DIR_TOS,dirh->fc.dev);
  681.     else flag=0;
  682.     if(!dirh->fc.index)return EACCDN;
  683.     entry=dirh->index % NR_DIR_ENTRIES;
  684.     chunk=dirh->index / NR_DIR_ENTRIES;    
  685.     read_inode(dirh->fc.index,&rip,dirh->fc.dev);
  686.     incr=psblk->increment;
  687.  
  688.     while( (limit=cnext_zone(&rip,chunk,&tmp,dirh->fc.dev)/DIR_ENTRY_SIZE) )
  689.     {
  690.         while( entry < limit)
  691.           {
  692.             dir_struct *try=&tmp->bdir[entry];
  693.             entry+=incr;
  694.             if(try->d_inum)
  695.             {
  696.                 char *tmpnam;
  697.                 tmpnam=tosify(try->d_name,flag,MMAX_FNAME(incr));
  698.  
  699.                 if (dirh->flags==0)
  700.                 {
  701.                     namelen -= sizeof(long);
  702.                     if (namelen <= 0) return ERANGE;
  703.                     *((long *)name) = (long)try->d_inum;
  704.                     name += sizeof(long);
  705.                        }
  706.  
  707.                 strncpy(name,tmpnam,namelen);
  708.                 dirh->index=entry+chunk*NR_DIR_ENTRIES;
  709.             /* set up a file cookie for this entry */
  710.                 fc->dev = dirh->fc.dev;
  711.                 fc->aux = 0;
  712.                 fc->index = (long)try->d_inum;
  713.                 fc->fs = &minix_filesys;
  714.                 if(strlen(tmpnam) >= namelen) 
  715.                     return ENAMETOOLONG;
  716.                 /* If turbo mode set atime here: we'll only
  717.                  * change the cache here so it wont cause
  718.                  * lots of I/O
  719.                  */
  720.                 if( cache_mode==TURBO
  721.                     && super_ptr[dirh->fc.dev]->version
  722.                     && dirh->fc.dev > 1 )
  723.                                set_atime(&dirh->fc);
  724.                 return 0;
  725.             }
  726.         }
  727.         if(entry!=NR_DIR_ENTRIES)return ENMFIL;
  728.         else entry=0;
  729.         chunk++;
  730.     }
  731.     return ENMFIL;
  732. }
  733.  
  734. long m_rewinddir(dirh)
  735. DIR *dirh;
  736. {
  737.     dirh->index=0;
  738.     return 0;
  739. }
  740.  
  741. long m_closedir(dirh)
  742. DIR *dirh;
  743. {
  744.  
  745. /* Access time is set here if we aren't in TURBO cache mode. Otherwise we
  746.  * would be sync'ing on every dir read which would be far too slow. See note
  747.  * in set_atime().
  748.  */
  749.  
  750.     if( cache_mode!=TURBO && super_ptr[dirh->fc.dev]->version && 
  751.             dirh->fc.dev > 1 )
  752.     {
  753.         set_atime(&dirh->fc);
  754.         l_sync();
  755.     }
  756.  
  757.     dirh->fc.index=0;
  758.     return 0;
  759. }
  760.  
  761. /* Set the atime of a V2 inode for filesystems. There is a snag here: if the
  762.  * disk is changed then this is likely not to be written out before the whole
  763.  * cache is invalidated. So we set the status to '3' which means that it is
  764.  * not alerted if this is dirty when invalidated (hardly the end of the world
  765.  * if the atime is slightly wrong!)
  766.  */
  767. void set_atime(fc)
  768. fcookie *fc;
  769. {
  770.     d_inode *rip;
  771.     int *status;
  772.     rip=get_inode2(fc->index,fc->dev,&status,NOGUESS);
  773.     rip->i_atime=Unixtime(Timestamp(),Datestamp());
  774.     if(*status!=2) *status=3;
  775. }
  776.  
  777.  
  778. long m_rlabel(dir,name,namelen)
  779. fcookie *dir;
  780. char *name;
  781. int namelen;
  782. {
  783.     return EFILNF;
  784. }
  785.  
  786. long m_wlabel(dir,name)
  787. fcookie *dir;
  788. char *name;
  789. {
  790.     return EACCDN;
  791. }
  792.  
  793. long m_dfree(dir,buffer)
  794. fcookie *dir;
  795. long *buffer;
  796. {
  797.     super_info *psblk ;
  798.     psblk = super_ptr[dir->dev];
  799.     buffer[1] = psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
  800.     buffer[0] = buffer[1] - count_bits(psblk->zbitmap,buffer[1]+1)+1;
  801.     buffer[2]=512L;
  802.     buffer[3]=2L;
  803.     return(0);
  804. }
  805.  
  806. long m_fscntl(dir,name,cmd,arg)
  807. fcookie *dir;
  808. char *name;
  809. int cmd;
  810. long arg;
  811. {
  812.     FILEPTR *f;
  813.     mfs_info *inf;
  814.     super_info *psblk;
  815.     long inum;
  816.     int uid,gid,id;
  817.     d_inode rip;
  818.     extern long init_addr;
  819.  
  820.     uid = Geteuid();
  821.     gid = Getegid();
  822.  
  823.     switch(cmd)
  824.     {
  825.         case MFS_VERIFY:
  826.         *((long *)arg)=MFS_MAGIC;
  827.         return 0;
  828.  
  829.         /* Sync the filesystem */
  830.         case MFS_SYNC:
  831.         TRACE("Done l_sync()");
  832.         l_sync();
  833.         return 0;
  834.  
  835.         /* Invalidate all cache entries for a given drive */
  836.         case MFS_CINVALID:
  837.         if(uid) return EACCDN;
  838.         m_invalidate(dir->dev);
  839.         return 0;
  840.  
  841.         /* Invalidate all fileptrs for a given drive */
  842.         case MFS_FINVALID:
  843.         if(uid) return EACCDN;
  844.         id=Getpid();
  845.         for(f=firstptr;f;f=f->next)if(f->fc.dev==dir->dev)m_close(f,id);
  846.         return 0;
  847.  
  848.         case MFS_INFO:
  849.         psblk=super_ptr[dir->dev];
  850.         inf=(mfs_info *)arg;
  851.         inf->total_zones=psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
  852.         inf->total_inodes=psblk->sblk.s_ninodes;
  853.         inf->version=psblk->version+1;
  854.         inf->increment=psblk->increment;        
  855.         inf->free_inodes=inf->total_inodes-
  856.             count_bits(psblk->ibitmap,inf->total_inodes+1)+1;
  857.         inf->free_zones=inf->total_zones-count_bits(psblk->zbitmap,inf->total_zones+1)+1;
  858.         return 0;
  859.  
  860.         case MFS_IMODE:
  861.         if(uid) return EACCDN;
  862.         inum=search_dir(name,dir->index,dir->dev,FIND);
  863.         if(inum < 0 ) return inum;
  864.         read_inode(inum,&rip,dir->dev);
  865.         rip.i_mode=arg;
  866.         write_inode(inum,&rip,dir->dev);
  867.         return 0;
  868.  
  869.         case MFS_GTRANS:
  870.         *((long *) arg)=fs_mode[dir->dev];
  871.         return 0;
  872.  
  873.         case MFS_STRANS:
  874.         if(uid) return EACCDN;
  875.         fs_mode[dir->dev]=*((long *)arg);
  876.         return 0;
  877.  
  878.         case MFS_PHYS:
  879.         *((struct phys_part *)arg)=ppart[dir->dev];
  880.         return 0;
  881.  
  882.         case MFS_IADDR:
  883.         *((long *)arg)=(long)&init_addr;
  884.         return 0;
  885.  
  886.         case MFS_UPDATE:
  887.         if(cache_mode!=TURBO) return -1;
  888.         switch(arg)
  889.         {
  890.             case 0:
  891.             return update_suspend;
  892.  
  893.             case 1:
  894.             TRACE("Minixfs: update suspended");
  895.             update_suspend=1;
  896.             return 0;
  897.  
  898.             case 2:
  899.             TRACE("Minixfs: update restarted");
  900.             update_suspend=0;
  901.             return 0;
  902.  
  903.             case 3:
  904.             return update_pid;
  905.  
  906.             default:
  907.             return EINVFN;
  908.         }
  909.  
  910. /* Mounting and umounting.
  911.  * This basically involves cookie translation when crossing the mount point.
  912.  * However this presents an interesting problem. Suppose E:\usr has D: mounted
  913.  * on it. If we are in directory E:\usr\ we are in the root directory of D:.
  914.  * If we do a cd \ then we get sent back to the root cookie directory of D:,
  915.  * which we want to be E:. The only way to change the root cookie at present is
  916.  * to force a disk change, this is no problem.
  917.  * The fun starts when we want to umount D:. Changing D: will have no effect
  918.  * because the root cookie of D: is now on E: and since E: hasn't changed the
  919.  * root cookie on D: wont change either. If we change E: then we will get the
  920.  * root cookie of D: changed but we also change E: as well.
  921.  * The only way out is to fiddle the aux field of the root cookie so it shows
  922.  * the original device. The cookie from m_root is copied with the dup_cookie
  923.  * function, if we kludge this at umount time to read the aux field and copy
  924.  * the original device the root cookies should be set back. This relies very
  925.  * heavily on the structure of filesys.c in MiNT, but it's better than nothing.
  926.  * Repeat after me: KLUDGE, KLUDGE, KLUDGE, KLUDGE, KLUDGE!!!
  927.  */
  928.  
  929.         case MFS_MOUNT:
  930.         {
  931.             char tpath[]="A:";
  932.             fcookie fc;
  933.             unsigned dev;
  934.             super_info *mptr;
  935.             if(uid) return EACCDN;
  936.             /* Lookup the entry */
  937.             if( (inum=m_lookup(dir,name,&fc)) ) return inum;
  938.             read_inode(fc.index,&rip,fc.dev);
  939.             /* Must be a directory */
  940.             if(!IS_DIR(rip))
  941.             {
  942.                 DEBUG("MFS_MOUNT: not a directory!");
  943.                 return EACCDN;
  944.             }
  945.  
  946.             if(fc.index==ROOT_INODE)
  947.             {
  948.                 DEBUG("MFS_MOUNT: can't mount on root!");
  949.                 return EACCDN;
  950.             }
  951.             if(!arg) return EINVFN;
  952.             dev = ( ( mfs_mount *) arg)->dev ;
  953.  
  954.             if(fc.dev==dev)
  955.             {
  956.                 DEBUG("MFS_MOUNT: can't mount on self!");
  957.                 return EACCDN;
  958.             }
  959.  
  960.             tpath[0]='A'+dev;
  961.  
  962.             /* Sync filesystem (and force access if unrecognised) */
  963.  
  964.             d_cntl(MFS_SYNC,tpath,0l);
  965.  
  966.             psblk=super_ptr[dev];
  967.  
  968.             if(!psblk || psblk==DFS)
  969.             {
  970.                 DEBUG("MFS_MOUNT: not a Minixfs filesystem!");
  971.                 return EACCDN;
  972.             }
  973.  
  974.             if(psblk->mnt_inode)
  975.             {
  976.               DEBUG("MFS_MOUNT: filesystem already mounted!");
  977.               return EACCDN;
  978.             }
  979.  
  980.             if(psblk->mnt_first)
  981.             {
  982.               DEBUG("MFS_MOUNT: filesystem has others mounted!");
  983.               return EACCDN;
  984.             }
  985.  
  986.             for(mptr=super_ptr[fc.dev]->mnt_first;
  987.                           mptr;mptr=mptr->mnt_next)
  988.             {
  989.               if( mptr->mnt_inode==fc.index )
  990.               {
  991.                 DEBUG("MFS_MOUNT: inode already mounted on!");
  992.                 return EACCDN;
  993.               }
  994.             }
  995.  
  996.             mptr=super_ptr[fc.dev];
  997.  
  998.             psblk->mnt_next=mptr->mnt_first;
  999.             mptr->mnt_first=psblk;
  1000.  
  1001.             psblk->mnt_dev=fc.dev;
  1002.             psblk->mnt_inode=fc.index;
  1003.  
  1004.             psblk->mnt_flags |= MNT_CHANGE;
  1005.  
  1006.             /* Force change */
  1007.             d_lock(1,dev);
  1008.             d_lock(0,dev);
  1009.  
  1010.             d_cntl(MFS_SYNC,tpath,0l);
  1011.  
  1012.             psblk->mnt_flags &= ~MNT_CHANGE;
  1013.  
  1014.             return 0;
  1015.         }
  1016.  
  1017.         case MFS_UMOUNT:
  1018.         {
  1019.             fcookie fc;
  1020.             super_info **pptr,*pdev;
  1021.             char tpath[]="A:";
  1022.  
  1023.             /* Lookup path */
  1024.             if( ( inum=m_lookup(dir,name,&fc) ) ) return inum;
  1025.  
  1026.             psblk=super_ptr[fc.dev];
  1027.  
  1028.             /* Can't have other devices mounted */
  1029.             if(psblk->mnt_first)
  1030.             {
  1031.                 DEBUG("MFS_UMOUNT: device busy");
  1032.                 return EACCDN;
  1033.             }
  1034.  
  1035.             if(!psblk->mnt_inode)
  1036.             {
  1037.                 DEBUG("MFS_UMOUNT: not mounted");
  1038.                 return EACCDN;
  1039.             }
  1040.  
  1041.             /* Unlink from parent filesystem list */
  1042.             for(pptr=&super_ptr[psblk->mnt_dev]->mnt_first;*pptr;
  1043.                               pptr=&(*pptr)->mnt_next )
  1044.             {
  1045.                 if(*pptr==psblk)
  1046.                 {
  1047.                     *pptr = psblk->mnt_next;
  1048.                     break;
  1049.                 }
  1050.             }
  1051.  
  1052.             pdev=psblk;
  1053.  
  1054.             /* Find root device */
  1055.             while(pdev->mnt_inode) pdev = super_ptr[pdev->mnt_dev];
  1056.  
  1057.             psblk->mnt_inode=0;
  1058.  
  1059.             pdev->mnt_flags |= MNT_CHANGE;
  1060.  
  1061.             /* Make dupcookie restore dev fields */
  1062.             restore_dev = psblk->dev;
  1063.  
  1064.             /* Change root device */
  1065.             d_lock(1,pdev->dev);
  1066.             d_lock(0,pdev->dev);
  1067.  
  1068.             tpath[0] = 'A'+pdev->dev;
  1069.             d_cntl(MFS_SYNC,tpath,0l);
  1070.  
  1071.             restore_dev = -1;
  1072.  
  1073.             pdev->mnt_flags &= ~MNT_CHANGE;
  1074.  
  1075.             return 0;
  1076.  
  1077.         }
  1078.  
  1079.         case FUTIME:
  1080.         case FTRUNCATE:
  1081.         {
  1082.             fcookie fc;
  1083.             read_inode(dir->index,&rip,dir->dev);
  1084.         /* Have we got 'x' access for current dir ? */
  1085.          if (check_mode(uid,gid,&rip,S_IXUSR)) 
  1086.           return EACCDN;
  1087.         /* Lookup the entry */
  1088.             if( (inum=m_lookup(dir,name,&fc)) ) return inum;
  1089.             read_inode(fc.index,&rip,fc.dev);
  1090.             if(cmd==FUTIME)
  1091.             {
  1092.                 short *timeptr = (short *) arg;
  1093.               /* The owner or super-user can always touch,
  1094.                  others only if timeptr == 0 and write
  1095.                  permission. */
  1096.               if (uid && uid != rip.i_uid
  1097.                   && (timeptr
  1098.                   || check_mode (uid, gid, &rip, S_IWUSR)))
  1099.                 return EACCDN;
  1100.                 
  1101.               rip.i_ctime = Unixtime(Timestamp (), Datestamp ());
  1102.               if(timeptr)
  1103.               {    
  1104.                 rip.i_atime = Unixtime(timeptr[0], timeptr[1]);
  1105.                 rip.i_mtime = Unixtime(timeptr[2], timeptr[3]);
  1106.               }
  1107.               else rip.i_atime = rip.i_mtime = rip.i_ctime;
  1108.                 write_inode (fc.index, &rip, fc.dev);
  1109.                 if (cache_mode != TURBO) l_sync();
  1110.                 return 0;
  1111.             }
  1112.  
  1113.             if(!IS_REG(rip)) return EACCDN;
  1114.             /* Need write access as well */
  1115.             if (check_mode(uid, gid, &rip, S_IWUSR))
  1116.               return EACCDN;
  1117.             itruncate(fc.index,fc.dev,*((long *)arg));
  1118.               if (cache_mode != TURBO) l_sync ();
  1119.             return 0;
  1120.         }
  1121.  
  1122.         default:
  1123.         return EINVFN;
  1124.     }
  1125. }
  1126.  
  1127. /* m_rename, move a file or directory. Directories need special attention 
  1128.  * because if /usr/foo is moved to /usr/foo/bar then the filesystem will be
  1129.  * damaged by making the /usr/foo directory inaccessible. The sanity checking
  1130.  * performed is very simple but should cover all cases: Start at the parent
  1131.  * of the destination , check if this is the source inode , if not then 
  1132.  * move back to '..' and check again , repeatedly check until the root inode
  1133.  * is reached , if the source is ever seen on the way back to the root then
  1134.  * the rename is invalid , otherwise it should be OK.
  1135.  */
  1136.  
  1137. long m_rename(olddir,oldname,newdir,newname)
  1138. fcookie *olddir;
  1139. char *oldname;
  1140. fcookie *newdir;
  1141. char *newname;
  1142. {
  1143.     long finode,ret;
  1144.     d_inode rip;
  1145.     long pos;
  1146.     char dirmove,dirren;
  1147.     dirmove=0;
  1148.     dirren=0;
  1149. /* Check cross drives */
  1150.     if(olddir->dev!=newdir->dev)return EXDEV;
  1151.  
  1152. /* Check new doesn't exist and path is otherwise valid */
  1153.     finode=search_dir(newname,newdir->index,newdir->dev,FIND);
  1154.     if(finode>0) return EACCDN;
  1155.     if(finode!=EFILNF) return finode;
  1156.  
  1157. /* Check old path OK */
  1158.     if((finode=search_dir(oldname,olddir->index,olddir->dev,FIND))<0)
  1159.         return finode;
  1160.  
  1161.     read_inode(finode,&rip,olddir->dev);
  1162.  
  1163. /* Sanity check movement of directories */
  1164.     if(IS_DIR(rip))
  1165.     {
  1166.         dirren=1;
  1167.          if(olddir->index!=newdir->index)
  1168.         {
  1169. #ifdef MFS_NMOVE_DIR
  1170.             return EACCDN;
  1171. #else
  1172.             d_inode riptemp;
  1173.             ret=is_parent(newdir->index,finode,olddir->dev);
  1174.             if(ret < 0) return ret;
  1175.             if(ret) return EACCDN;
  1176.             read_inode(newdir->index,&riptemp,newdir->dev);
  1177.             if(riptemp.i_nlinks==MINIX_MAX_LINK) return EACCDN;
  1178.             TRACE("minixfs: valid directory move");
  1179.             dirmove=1;
  1180. #endif
  1181.         }
  1182.     }
  1183.  
  1184.     /* Check the m_getname cache is not invalidated by this move ....
  1185.     if no dir move, invalidate if the ldir is the name being changed ,
  1186.     if we move dir's then if the olddir is a parent of ldir, invalidate */
  1187.  
  1188.     if (lpath && ldir.dev == olddir->dev
  1189.         && (ldir.index == finode
  1190.         || (dirmove && is_parent (ldir.index, finode, olddir->dev) > 0)))
  1191.     {
  1192.         Kfree (lpath);
  1193.         lpath=0;
  1194.     }
  1195.         
  1196. /* Create new entry */
  1197.       if((pos=search_dir(newname,newdir->index,newdir->dev,ADD))<0) return pos;
  1198. /* Delete old path */
  1199.     if((finode=search_dir(oldname,olddir->index,olddir->dev,KILL))<0)
  1200.             return finode;
  1201.     {
  1202.       unshort ino = finode;
  1203.       l_write (newdir->index, pos, 2L, &ino, newdir->dev);
  1204.     }
  1205.  
  1206. /* When moving directories, fixup things like '..' and nlinks of old and
  1207.  * new dirs
  1208.  */
  1209.  
  1210.     if(dirmove)
  1211.     {
  1212.         pos=search_dir("..",finode,newdir->dev,POS);
  1213.         if(pos<0) 
  1214.         {
  1215.             ALERT("m_rename: no .. in inode %ld",finode);
  1216.             return EACCDN;
  1217.         }
  1218.         if(pos!=DIR_ENTRY_SIZE*super_ptr[newdir->dev]->increment)
  1219.           ALERT("m_rename: Unexpected .. position in inode %ld",finode);
  1220.         {
  1221.           unshort ino = newdir->index;
  1222.           l_write (finode, pos, 2L, &ino, newdir->dev);
  1223.         }
  1224.         read_inode(olddir->index,&rip,olddir->dev);
  1225.         rip.i_nlinks--;
  1226.         write_inode(olddir->index,&rip,olddir->dev);
  1227.         read_inode(newdir->index,&rip,newdir->dev);
  1228.         rip.i_nlinks++;
  1229.         write_inode(newdir->index,&rip,newdir->dev);
  1230.     }
  1231.  
  1232.     if(cache_mode) l_sync();
  1233.  
  1234. /* Check the m_getname cache is not invalidated by this move ....
  1235.  * if no dir alter, invalidate if the ldir is the name being changed ,
  1236.  * if we alter dir's then if the moved dir is a parent of ldir, invalidate.
  1237.  */
  1238.  
  1239.     if (lpath && ldir.dev == olddir->dev
  1240.         && (ldir.index == finode
  1241.         || (dirren && is_parent (ldir.index, finode, olddir->dev) > 0)))
  1242.     {
  1243.         Kfree (lpath);
  1244.         lpath=0;
  1245.     }
  1246.  
  1247.     return 0;
  1248. }
  1249.  
  1250. /* Minix hard-link, you can't make a hardlink to a directory ... it causes
  1251.  * too much trouble, use symbolic links instead.
  1252.  */
  1253.  
  1254. long m_hardlink(fromdir,fromname,todir,toname)
  1255. fcookie *fromdir;
  1256. char *fromname;
  1257. fcookie *todir;
  1258. char *toname;
  1259. {
  1260.     long finode;
  1261.     d_inode rip;
  1262.     long pos;
  1263.  
  1264. /* Check cross drives */
  1265.     if(fromdir->dev!=todir->dev)return EXDEV;
  1266.  
  1267. /* Check new doesn't exist and path is otherwise valid */
  1268.     finode=search_dir(toname,todir->index,todir->dev,FIND);
  1269.     if(finode>0) return EACCDN;
  1270.     if(finode!=EFILNF) return finode;
  1271.  
  1272. /* Check old path OK */
  1273.     if((finode=search_dir(fromname,fromdir->index,fromdir->dev,FIND))<0)
  1274.         return finode;
  1275.  
  1276.     read_inode(finode,&rip,fromdir->dev);
  1277.     if(!IS_REG(rip) || (rip.i_nlinks >=MINIX_MAX_LINK) ) return EACCDN;
  1278.  
  1279. /* Create new entry */
  1280.     if((pos=search_dir(toname,todir->index,todir->dev,ADD))<0) return pos;
  1281.     {
  1282.       unshort ino = finode;
  1283.       l_write (todir->index, pos, 2L, &ino, todir->dev);
  1284.     }
  1285.     rip.i_nlinks++;
  1286.     rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  1287.     write_inode(finode,&rip,fromdir->dev);
  1288.  
  1289.     if(cache_mode) l_sync();
  1290.  
  1291.     return 0;
  1292. }
  1293.  
  1294. /* Symbolic links ... basically similar to a regular file with one zone */
  1295.  
  1296. long m_symlink(dir,name,to)
  1297. fcookie *dir;
  1298. char *name;
  1299. char *to;
  1300. {
  1301.     d_inode rip;
  1302.     long pos;
  1303.     unshort newinode;
  1304.  
  1305.     if(!*to)
  1306.     {
  1307.         DEBUG("m_symlink: invalid null filename");
  1308.         return EACCDN;
  1309.     }
  1310.  
  1311.     if(strlen(to)>=SYMLINK_NAME_MAX)
  1312.     {
  1313.         DEBUG("minixfs: Symbolic link name too long");        
  1314.         return ERANGE;
  1315.     }
  1316.  
  1317.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0) return pos;
  1318.  
  1319.     if(!(newinode=alloc_inode(dir->dev)))
  1320.     {
  1321.         DEBUG("minixfs: symlink drive %c,no free inodes",dir->dev+'A');
  1322.         return EACCDN;
  1323.     }
  1324.     
  1325.  
  1326.     bzero(&rip,sizeof(d_inode));
  1327.     rip.i_mode=I_SYMLINK | 0777;
  1328.     rip.i_size=strlen(to)+1;
  1329.     rip.i_uid=Getuid();
  1330.     rip.i_gid=Getgid();
  1331.     rip.i_mtime=Unixtime(Timestamp(),Datestamp());
  1332.     rip.i_ctime=rip.i_mtime;
  1333.     rip.i_atime=rip.i_mtime;
  1334.     rip.i_nlinks=1;
  1335.  
  1336.     if(!(rip.i_zone[0]=alloc_zone(dir->dev)))
  1337.     {
  1338.         free_inode(newinode,dir->dev);
  1339.         DEBUG("minixfs: symlink drive %c no free zones",dir->dev+'A');
  1340.         return EACCDN;
  1341.     }
  1342.     btos_cpy((char *)&temp,to);
  1343.      write_zone(rip.i_zone[0],&temp,dir->dev,&syscache);
  1344.     write_inode(newinode,&rip,dir->dev);
  1345.     l_write(dir->index,pos,2L,&newinode,dir->dev);
  1346.  
  1347.     if(cache_mode) l_sync();
  1348.  
  1349.     return 0;
  1350. }
  1351.  
  1352. long m_readlink(file,buf,len)
  1353. fcookie *file;
  1354. char *buf;
  1355. int len;
  1356. {
  1357.     long inum = file->index;
  1358.     d_inode rip;
  1359.  
  1360.     read_inode(inum,&rip,file->dev);
  1361.     if( (rip.i_mode & I_TYPE)!=I_SYMLINK)
  1362.     {
  1363.         DEBUG("minixfs: attempted readlink on non-symlink");
  1364.         return EACCDN;
  1365.     }
  1366.     read_zone(rip.i_zone[0],&temp,file->dev,&syscache);
  1367.     if(stob_ncpy(buf, (char *) &temp,len))
  1368.     {
  1369.         DEBUG("m_readlink: name too long");
  1370.         return ERANGE;
  1371.     }
  1372.     TRACE("m_readlink returned %s",buf);
  1373.  
  1374.     return 0;
  1375. }
  1376.  
  1377. /* the only settable attribute is FA_RDONLY; if the bit is set,
  1378.  * the mode is changed so that no write permission exists
  1379.  */
  1380. long m_chattr(file,attr)
  1381. fcookie *file;
  1382. int attr;
  1383. {
  1384.         long inum = file->index;
  1385.     int drive = file->dev;
  1386.     d_inode rip;
  1387.  
  1388.     if ( (attr & FA_RDONLY) ) {
  1389.         read_inode(inum,&rip,drive);
  1390.         rip.i_mode &= ~(0222);    /* turn off write permission */
  1391.         rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  1392.         write_inode(inum,&rip,drive);
  1393.         if(cache_mode) l_sync();
  1394.     } else if (attr == 0) {
  1395.         read_inode(inum,&rip,drive);
  1396.         if ( (rip.i_mode & 0222) == 0 ) {
  1397.             rip.i_mode |= ( (rip.i_mode&0444) >> 1 );
  1398.                 /* turn write permission back on */
  1399.             rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  1400.             write_inode(inum,&rip,drive);
  1401.             if(cache_mode) l_sync();
  1402.         }
  1403.     }
  1404.     return 0;
  1405. }
  1406.  
  1407. long m_pathconf(dir,which)
  1408. fcookie *dir;
  1409. int which;
  1410. {
  1411.     switch(which) {
  1412.     case -1:
  1413.         return DP_MAXREQ;
  1414.     case DP_IOPEN:
  1415.         return UNLIMITED;
  1416.     case DP_MAXLINKS:
  1417.          return MINIX_MAX_LINK;
  1418.     case DP_PATHMAX:
  1419.         return UNLIMITED; /* At last ! */
  1420.     case DP_NAMEMAX:
  1421.         return MMAX_FNAME(super_ptr[dir->dev]->increment);
  1422.     case DP_ATOMIC:
  1423.         return BLOCK_SIZE;    /* we can write at least a block atomically */
  1424.     case DP_TRUNC:
  1425.         return DP_AUTOTRUNC;
  1426.     case DP_CASE:
  1427.         return DP_CASESENS;    /* Well sort of ... */
  1428.     default:
  1429.         return EINVFN;
  1430.     }
  1431. }
  1432.  
  1433. long m_release(fc)
  1434. fcookie *fc;
  1435. {
  1436.     return 0;
  1437. }
  1438.  
  1439. long m_dupcookie(dest,src)
  1440. fcookie *dest,*src;
  1441. {
  1442.     unsigned tmpaux;
  1443.     tmpaux=dest->aux;
  1444.     *dest=*src;
  1445.     if(restore_dev!=-1 && 
  1446.     (tmpaux & (AUX_DEV|AUX_DRV))== restore_dev|AUX_DRV )
  1447.                         dest->dev = tmpaux & AUX_DEV;
  1448.  
  1449.     return 0;
  1450. }
  1451.